package org.micro.frame.service.global;

import lombok.extern.slf4j.Slf4j;
import org.micro.frame.commons.Message;
import org.micro.frame.service.exception.UnauthorizedException;
import org.springframework.core.MethodParameter;
import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.NoHandlerFoundException;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;

import javax.servlet.http.HttpServletRequest;

/**
 * 用于预处理httpMessageConverter
 *
 * @author lry
 */
@Slf4j
@RestControllerAdvice
public class GlobalControllerHandler implements ResponseBodyAdvice {

    @Override
    public boolean supports(MethodParameter returnType, Class converterType) {
        return true;
    }

    @Override
    public Object beforeBodyWrite(
            Object body, MethodParameter returnType, MediaType selectedContentType,
            Class selectedConverterType, ServerHttpRequest request, ServerHttpResponse response) {
        if (returnType.hasMethodAnnotation(ExceptionHandler.class)) {
            return body;
        } else {
            // The build success result
            return this.buildSuccessMessage(body);
        }
    }

    @ExceptionHandler(value = Throwable.class)
    public Message allExceptionHandler(HttpServletRequest request, Exception e) {
        if (e instanceof NoHandlerFoundException) {
            return this.buildFailMessage(HttpStatus.BAD_REQUEST);
        } else if (e instanceof UnauthorizedException) {
            return this.buildFailMessage(HttpStatus.UNAUTHORIZED);
        } else {
            log.error("Internal Server Error: " + e.getMessage(), e);
            return this.buildFailMessage(HttpStatus.INTERNAL_SERVER_ERROR);
        }
    }

    /**
     * The build success message
     *
     * @param data data
     * @return Message
     */
    private Message buildSuccessMessage(Object data) {
        return Message.buildMessage(true, HttpStatus.OK.value(), HttpStatus.OK.getReasonPhrase(), data);
    }

    /**
     * The build failure message
     *
     * @param httpStatus HttpStatus
     * @return Message
     */
    private Message buildFailMessage(HttpStatus httpStatus) {
        return Message.buildMessage(false, httpStatus.value(), httpStatus.getReasonPhrase(), null);
    }

}